SPB Git forge

spb/websensor

Public
33commits 1branches 0releases
3.4 MBsize
maindefault branch
10 days agolast push
TypeScript 55.4% Python 43.2% SQL 1.2%
6.9 KB · 102 lines tsx
Raw Blame History
1import Link from "next/link";2import type { Metadata } from "next";3import { notFound } from "next/navigation";4import { Bar, Chip, Flag, PageHeader, Panel, Stat, Table, Td } from "@/components/ui";5import { api } from "@/lib/api";6import { fmtInt, fmtPct, relTime } from "@/lib/format";78export const dynamic = "force-dynamic";910export async function generateMetadata({ params }: { params: Promise<{ sector: string }> }): Promise<Metadata> {11  const { sector } = await params;12  const c = await api.coverageSector(sector);13  if (!c) return { title: "Coverage" };14  return { title: `${c.label} · coverage ${c.score.toFixed(1)}`, description: `${fmtInt(c.covered)} of ${fmtInt(c.members)} ${c.label.toLowerCase()} organizations observed by WebSensor (${c.universes.length} universes).`, alternates: { canonical: `/coverage/${c.sector}` } };15}1617function band(score: number): string {18  return score >= 80 ? "text-signal" : score >= 60 ? "text-high" : score >= 40 ? "text-warn" : "text-fg-muted";19}2021type Params = { u?: string; status?: string };2223export default async function CoverageSectorPage({ params, searchParams }: { params: Promise<{ sector: string }>; searchParams: Promise<Params> }) {24  const { sector } = await params;25  const sp = await searchParams;26  const c = await api.coverageSector(sector);27  if (!c) notFound();28  const universes = sp.u ? c.universes.filter((u) => u.key === sp.u) : c.universes;29  const filter = (m: { covered: boolean; shadow: number; seed_status: string | null }): boolean => {30    if (sp.status === "observed") return m.covered;31    if (sp.status === "shadow") return !m.covered && m.shadow > 0;32    if (sp.status === "queued") return !m.covered && m.shadow === 0 && (m.seed_status === "queued" || m.seed_status === "discovering");33    if (sp.status === "missing") return !m.covered && m.shadow === 0 && !(m.seed_status === "queued" || m.seed_status === "discovering");34    return true;35  };36  const href = (patch: Params): string => {37    const n = { ...sp, ...patch };38    const q = new URLSearchParams();39    if (n.u) q.set("u", n.u);40    if (n.status) q.set("status", n.status);41    const s = q.toString();42    return `/coverage/${c.sector}${s ? `?${s}` : ""}`;43  };44  return (45    <>46      <PageHeader47        compact48        kicker={<Link href="/coverage" className="hover:underline">Coverage</Link>}49        title={<span>{c.label} <span className={`font-mono ${band(c.score)}`}>{c.score.toFixed(1)}</span></span>}50        description={`${c.description ?? ""} ${fmtInt(c.covered)} of ${fmtInt(c.members)} organizations observed · breadth ${fmtPct(c.breadth)} · depth ${fmtPct(c.depth)} · ${fmtInt(c.sensors)} sensors matched · ${fmtInt(c.shadow_members)} in shadow · ${fmtInt(c.queued)} queued for discovery. Updated ${relTime(c.generated_at)}.`}51      />52      <div className="panel mb-4 grid grid-cols-2 divide-x divide-y divide-line sm:grid-cols-3 lg:grid-cols-6 lg:divide-y-0">53        <Stat label="Score" value={<span className={band(c.score)}>{c.score.toFixed(1)}</span>} hint={`weight ×${c.weight}`} />54        <Stat label="Breadth" value={fmtPct(c.breadth)} hint="importance-weighted share observed" />55        <Stat label="Depth" value={fmtPct(c.depth)} hint="sensors per organization / 5" />56        <Stat label="Observed" value={`${fmtInt(c.covered)} / ${fmtInt(c.members)}`} tone="signal" />57        <Stat label="Shadow" value={fmtInt(c.shadow_members)} tone="info" hint="validated, proving signal" />58        <Stat label="Countries" value={fmtInt(c.by_country.filter((x) => x.country !== "—").length)} hint={`${c.universes.length} universes`} />59      </div>60      <div className="mb-2 flex flex-wrap items-center gap-1">61        <span className="label mr-1">status</span>62        {[["", "all"], ["observed", "observed"], ["shadow", "shadow"], ["queued", "queued"], ["missing", "not yet seeded"]].map(([k, l]) => (63          <Chip key={k} href={href({ status: k || undefined })} tone={(sp.status ?? "") === k ? "signal" : "default"}>{l}</Chip>64        ))}65        <span className="label mx-1">·</span>66        <span className="label mr-1">universe</span>67        <Chip href={href({ u: undefined })} tone={!sp.u ? "signal" : "default"}>all</Chip>68        {c.universes.map((u) => (69          <Chip key={u.key} href={href({ u: u.key })} tone={sp.u === u.key ? "signal" : "default"}>{u.label} <span className="font-mono text-[10.5px] text-fg-subtle">{u.covered}/{u.members}</span></Chip>70        ))}71      </div>72      {universes.map((u) => {73        const items = u.items.filter(filter);74        return (75          <Panel key={u.key} dense className="mb-4" title={<span>{u.label} <span className={`font-mono text-[12px] ${band(u.score)}`}>{u.score.toFixed(1)}</span> <span className="font-mono text-[11px] text-fg-subtle">· {u.covered}/{u.members} observed · {fmtInt(u.sensors)} sensors</span></span>} action={u.provenance ? <span className="max-w-[40ch] truncate text-[10.5px] text-fg-subtle" title={u.provenance}>{u.provenance}</span> : undefined}>76            {items.length === 0 ? (77              <div className="p-3 text-[12px] text-fg-subtle">No member matches this filter.</div>78            ) : (79              <div className="max-h-[32rem] overflow-auto">80                <Table head={["Organization", "Domain", "Country", "Imp.", "Status", "Sensors", "Depth", ""]}>81                  {items.map((m) => (82                    <tr key={`${u.key}/${m.domain}`} className="hover:bg-panel-2/60">83                      <Td>{m.source_id ? <Link href={`/source/${m.source_id}`} className="font-medium hover:underline">{m.name}</Link> : <span className="font-medium">{m.name}</span>}</Td>84                      <Td mono><Link href={`/domain/${m.domain}`} className="text-fg-muted hover:underline">{m.domain}</Link></Td>85                      <Td>{m.country ? <span className="inline-flex items-center gap-1 font-mono text-[11px] text-fg-muted"><Flag code={m.country} /> {m.country}</span> : <span className="text-fg-subtle">—</span>}</Td>86                      <Td mono className="text-fg-subtle">{m.importance}</Td>87                      <Td>{m.covered ? <Chip tone="ok">observed</Chip> : m.shadow > 0 ? <Chip tone="info">shadow ×{m.shadow}</Chip> : m.seed_status === "queued" || m.seed_status === "discovering" ? <Chip tone="warn">{m.seed_status}</Chip> : m.seed_status === "blocked" ? <Chip tone="danger">blocked</Chip> : m.seed_status ? <Chip>{m.seed_status}</Chip> : <Chip>not seeded</Chip>}</Td>88                      <Td mono>{m.sensors}{m.shadow ? <span className="text-fg-subtle"> +{m.shadow}</span> : null}</Td>89                      <Td><div className="w-16"><Bar value={m.depth * 100} tone={m.depth >= 0.8 ? "signal" : m.depth >= 0.4 ? "mid" : "info"} /></div></Td>90                      <Td>{m.source_id && <Link href={`/source/${m.source_id}`} className="whitespace-nowrap text-[11px] text-fg-subtle hover:text-fg">source →</Link>}</Td>91                    </tr>92                  ))}93                </Table>94              </div>95            )}96          </Panel>97        );98      })}99    </>100  );101}102